/*
 * Copyright (C) 2010 Daniel Sundberg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package nu.danielsundberg.droid.spinbox;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import nu.danielsundberg.droid.spinbox.objects.Cube;
import android.content.SharedPreferences;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.opengl.GLU;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

/**
 * 
 * @author Daniel Sundberg
 *
 */
public class SpinboxWallpaperService extends GlWallpaperService {

	public static final String PREFERENCES = "nu.danielsundberg.droid.spinbox.livewallpaper";
		
	public SpinboxWallpaperService() {
		super();
	}

	public Engine onCreateEngine() {
		MyEngine engine = new MyEngine();
		return engine;
	}


	class MyEngine extends GLEngine implements SharedPreferences.OnSharedPreferenceChangeListener, SensorEventListener {
		MyRenderer renderer;
		public MyEngine() {
			super();
			// handle prefs, other initialization
			renderer = new MyRenderer();
			setRenderer(renderer);
			setRenderMode(RENDERMODE_CONTINUOUSLY);
		}

		public void onDestroy() {
			// Unregister this as listener
			sm.unregisterListener(this);			
			
			// Kill renderer
			if (renderer != null) {
				renderer.release(); // assuming yours has this method - it should!
			}
			renderer = null;
			
			setTouchEventsEnabled(false);

			super.onDestroy();
		}
		
		  private SensorManager sm;

		
		@Override
		public void onTouchEvent(MotionEvent event) {			
			super.onTouchEvent(event);
			renderer.onTouchEvent(event);			
		}

		@Override
		public void onCreate(SurfaceHolder surfaceHolder) {
			super.onCreate(surfaceHolder);
			
			// Add touch events
			setTouchEventsEnabled(true);

			// Get sensormanager and register as listener.
		    sm = (SensorManager) getSystemService(SENSOR_SERVICE);		
		    Sensor orientationSensor = sm.getDefaultSensor(SensorManager.SENSOR_ORIENTATION);	
		    sm.registerListener(this, orientationSensor, SensorManager.SENSOR_DELAY_GAME);
		
		}

		@Override
		public void onSharedPreferenceChanged(
				SharedPreferences sharedPreferences, String key) {
		}

		@Override
		public void onAccuracyChanged(Sensor sensor, int accuracy) {
		}

		@Override
		public void onSensorChanged(SensorEvent event) {
			renderer.onSensorChanged(event);
		}
	}
	
	class MyRenderer implements GlWallpaperService.Renderer {

		/** Cube instance */
		private Cube cube;	
		
		//private Room room;
		
		/* Rotation values */
		private float xrot;					//X Rotation
		private float yrot;					//Y Rotation

		private float xLightRot;
		private float zLightRot;
		
		/* Rotation speed values */
		private float xspeed;				//X Rotation Speed ( NEW )
		private float yspeed;				//Y Rotation Speed ( NEW )
		
		private float xLightSpeed;
		private float zLightSpeed;
		
		private float z = -5.0f;			//Depth Into The Screen ( NEW )
		
		private int filter = 1;				//Which texture filter? ( NEW )
		
		public void onSensorChanged(SensorEvent event) {
			
			if(event.sensor.getType() == SensorManager.SENSOR_ORIENTATION) {
				
				float x = event.values[0];
				float z = event.values[2];

	    	    xLightRot = x;
	    	    zLightRot = z;    
				
				Log.d("Renderer", "Sensor_Orientation event x:"+lightPosition[0]+" y:"+lightPosition[1]+" z:"+lightPosition[2]);
	    	    	
			} else {
				Log.d("Renderer", "SensorchangeEvent "+event.sensor.getType()+" :"+event.values[0]+":"+event.values[1]+":"+event.values[2]);
				
			}
			
			
		}
		
		
		private float[] lightAmbient = {0.5f, 0.5f, 0.5f, 1.0f};
		private float[] lightDiffuse = {1.0f, 1.0f, 1.0f, 1.0f};
		private float[] lightPosition = {1.0f, 2.0f, 3.0f, 1.0f};
			
		/* The buffers for our light values  */
		private FloatBuffer lightAmbientBuffer;
		private FloatBuffer lightDiffuseBuffer;
		private FloatBuffer lightPositionBuffer;
		
		private float oldX;
	    private float oldY;
	    
	    private float oldLightX;
	    private float oldLightZ;
	    
	    //Proved to be good for normal rotation		
		private final float TOUCH_SCALE = 0.2f;		
		
		public MyRenderer() {
			//
			ByteBuffer byteBuf = ByteBuffer.allocateDirect(lightAmbient.length * 4);
			byteBuf.order(ByteOrder.nativeOrder());
			lightAmbientBuffer = byteBuf.asFloatBuffer();
			lightAmbientBuffer.put(lightAmbient);
			lightAmbientBuffer.position(0);
			
			byteBuf = ByteBuffer.allocateDirect(lightDiffuse.length * 4);
			byteBuf.order(ByteOrder.nativeOrder());
			lightDiffuseBuffer = byteBuf.asFloatBuffer();
			lightDiffuseBuffer.put(lightDiffuse);
			lightDiffuseBuffer.position(0);
			
			byteBuf = ByteBuffer.allocateDirect(lightPosition.length * 4);
			byteBuf.order(ByteOrder.nativeOrder());
			lightPositionBuffer = byteBuf.asFloatBuffer();
			lightPositionBuffer.put(lightPosition);
			lightPositionBuffer.position(0);
			
			
			cube = new Cube();
			
			//room = new Room();
			
			
		}

		public void release() {
			// TODO stuff to release
			
		}

		/**
		 * The Surface is created/init()
		 */
		public void onSurfaceCreated(GL10 gl, EGLConfig config) {		
			//And there'll be light!
			gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbientBuffer);		//Setup The Ambient Light ( NEW )
			gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuseBuffer);		//Setup The Diffuse Light ( NEW )
			gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPositionBuffer);	//Position The Light ( NEW )
			gl.glEnable(GL10.GL_LIGHT0);											//Enable Light 0 ( NEW )

			//Settings
			gl.glDisable(GL10.GL_DITHER);				//Disable dithering ( NEW )
			gl.glEnable(GL10.GL_TEXTURE_2D);			//Enable Texture Mapping
			gl.glShadeModel(GL10.GL_SMOOTH); 			//Enable Smooth Shading
			gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); 	//Black Background
			gl.glClearDepthf(1.0f); 					//Depth Buffer Setup
			gl.glEnable(GL10.GL_DEPTH_TEST); 			//Enables Depth Testing
			gl.glDepthFunc(GL10.GL_LEQUAL); 			//The Type Of Depth Testing To Do
			
			gl.glEnable(GL10.GL_NORMALIZE);				// Normalaaais
			
			gl.glCullFace(GL10.GL_BACK);                                  // Set Culling Face To Back Face
		    gl.glEnable(GL10.GL_CULL_FACE);                               // Enable Culling
			
			//Really Nice Perspective Calculations
			gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); 
					
			//Load the texture for the cube once during Surface creation
			cube.loadGLTexture(gl, getApplicationContext());
			
			//Load texture for room.
			//room.loadGLTexture(gl, getApplicationContext());
		}

		/**
		 * Here we do our drawing
		 */
		public void onDrawFrame(GL10 gl) {
			// Calculate new light position
			lightPosition[0] = xLightRot;
			//lightPosition[1] = yLightPos;
			lightPosition[2] = zLightRot;
			
			lightPositionBuffer.clear();
			lightPositionBuffer.put(lightPosition);
			lightPositionBuffer.position(0);

			
			//Clear Screen And Depth Buffer
			gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);	
			gl.glLoadIdentity();					//Reset The Current Modelview Matrix
			
			
			//Drawing
			gl.glTranslatef(0.0f, 0.0f, z);			//Move z units into the screen
			gl.glScalef(0.5f, 0.5f, 0.5f); 			//Scale the Cube to 80 percent, otherwise it would be too large for the screen
		
			//Check if the light flag has been set to enable/disable lighting
			gl.glEnable(GL10.GL_LIGHTING);
			gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPositionBuffer);
			
				
			//Rotate around the axis based on the rotation matrix (rotation, x, y, z)
			gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);	//X
			gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f);	//Y
		
			//Draw the Cube	
			filter = Integer.parseInt(getSharedPreferences(SpinboxWallpaperService.PREFERENCES, 0).getString("texture", "2"));			
			cube.draw(gl, filter);					
			
			/*
			gl.glLoadIdentity();					//Reset The Current Modelview Matrix
			
			gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);	//X
			gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f);	//Y
			
			// Draw room
			room.draw(gl, 0);
			*/
			
			//Change rotation factors
			xrot += xspeed;
			yrot += yspeed;
			
			xLightRot += xLightSpeed;
			zLightRot += zLightSpeed;
			
			
		}		

		/**
		 * If the surface changes, reset the view
		 */
		public void onSurfaceChanged(GL10 gl, int width, int height) {
			if(height == 0) { 						//Prevent A Divide By Zero By
				height = 1; 						//Making Height Equal One
			}

			gl.glViewport(0, 0, width, height); 	//Reset The Current Viewport
			gl.glMatrixMode(GL10.GL_PROJECTION); 	//Select The Projection Matrix
			gl.glLoadIdentity(); 					//Reset The Projection Matrix

			//Calculate The Aspect Ratio Of The Window
			GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f);

			gl.glMatrixMode(GL10.GL_MODELVIEW); 	//Select The Modelview Matrix
			gl.glLoadIdentity(); 					//Reset The Modelview Matrix
		}
			
		/**
		 * Override the touch screen listener.
		 * 
		 * React to moves and presses on the touchscreen.
		 */
		public boolean onTouchEvent(MotionEvent event) {
			
			
			//
			float x = event.getX();
	        float y = event.getY();
	        
	        //If a touch is moved on the screen
	        if(event.getAction() == MotionEvent.ACTION_MOVE) {
	        	
	        	//Calculate the change
	        	float dx = x - oldX;
		        float dy = y - oldY;
	        	
	        	//Rotate around the axis otherwise
	        
	    	    xrot += dy * TOUCH_SCALE;
	    	    yrot += dx * TOUCH_SCALE;    
	        
	    	    Log.d("Renderer", "Action_Move touchevent:"+event.getAction()+":"+xrot+":"+yrot);
	    	    
	        //A press on the screen
	        } else if(event.getAction() == MotionEvent.ACTION_UP) {
	        }
	        
	        //Remember the values
	        oldX = x;
	        oldY = y;
	        
	        //We handled the event
			return true;
		}
		
		
		
	}
	
}
		